using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Collections.Specialized;

namespace commonV2
{
    /// <summary>
    /// Hash algorithms
    /// http://searchvb.techtarget.com/tip/0,289483,sid8_gci1223163,00.html
    /// </summary>
    public enum HashType : int
    {
        SHA1,
        SHA256,
        SHA384,
        SHA512,
        MD5,
        RIPEMD160
    }

    public static class Hash
    {
        public static string MakeHash(string input)
        { 
            return MakeHash(input,HashType.MD5);
        }

        public static string MakeHash(string input, HashType hashtype)
        {
            input = input.Replace(" ", "");

            Byte[] clearBytes;
            Byte[] hashedBytes;
            string output = String.Empty;

            switch (hashtype)
            {
                case HashType.RIPEMD160:
                    clearBytes = new UTF8Encoding().GetBytes(input);
                    RIPEMD160 myRIPEMD160 = RIPEMD160Managed.Create();
                    hashedBytes = myRIPEMD160.ComputeHash(clearBytes);
                    output = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
                    break;
                case HashType.MD5:
                    clearBytes = new UTF8Encoding().GetBytes(input);
                    hashedBytes = ((HashAlgorithm)CryptoConfig.CreateFromName("MD5")).ComputeHash(clearBytes);
                    output = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
                    break;
                case HashType.SHA1:
                    clearBytes = Encoding.UTF8.GetBytes(input);
                    SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
                    sha1.ComputeHash(clearBytes);
                    hashedBytes = sha1.Hash;
                    sha1.Clear();
                    output = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
                    break;
                case HashType.SHA256:
                    clearBytes = Encoding.UTF8.GetBytes(input);
                    SHA256 sha256 = new SHA256Managed();
                    sha256.ComputeHash(clearBytes);
                    hashedBytes = sha256.Hash;
                    sha256.Clear();
                    output = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
                    break;
                case HashType.SHA384:
                    clearBytes = Encoding.UTF8.GetBytes(input);
                    SHA384 sha384 = new SHA384Managed();
                    sha384.ComputeHash(clearBytes);
                    hashedBytes = sha384.Hash;
                    sha384.Clear();
                    output = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
                    break;
                case HashType.SHA512:
                    clearBytes = Encoding.UTF8.GetBytes(input);
                    SHA512 sha512 = new SHA512Managed();
                    sha512.ComputeHash(clearBytes);
                    hashedBytes = sha512.Hash;
                    sha512.Clear();
                    output = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
                    break;
            }
            return output;
        }

        public static string GetHashOfFile(string fileName)
        {
            
            using (var cryptoProvider = new SHA1Managed())
            {
                using (var stream = File.OpenRead(fileName))
                {
                    return BitConverter.ToString(cryptoProvider.ComputeHash(stream));
                }
            }
        }
        public static myTypes.FileList GetChangeList(string checkDirectory, string hashFile)
        {
            myTypes.FileList changedFileList = new myTypes.FileList();
            string buffer = "";
            StreamReader file = File.OpenText(hashFile);
            char[] splitOptions = new char[] { Consts.constTab };
            while (!file.EndOfStream)
            {
                buffer = file.ReadLine();
                if (buffer.StartsWith(Consts.constCommentMarker)) continue;

                string[] items = buffer.Split(splitOptions, StringSplitOptions.RemoveEmptyEntries);
                if (items.Length < 3) continue;

                string fileName = fileFuncs.Combine(checkDirectory, items[0]);
                if (!File.Exists(fileName))
                {
                    changedFileList.Add(new myTypes.FileInfo(items[0], int.Parse(items[2]), items[1]));   
                    continue;
                }
                if (items[1] != Hash.GetHashOfFile(fileName))
                {
                    string tmp = Hash.GetHashOfFile(fileName);

                    changedFileList.Add(new myTypes.FileInfo(items[0], int.Parse(items[2]), items[1]));   
                    continue;
                }
            }
            file.Close();
            return changedFileList;
        }

        #region file hash
        public class FileHashOptions
        {
            public string Directory="", SearchPattern="";
            public bool Recursive = true;
            public string ResultHash = "";
            public void Copy(FileHashOptions op)
            {
                this.Directory = op.Directory;
                this.SearchPattern = op.SearchPattern;
                this.Recursive = op.Recursive;
            }
        }
        static FileHashOptions myHashOptions = null;
        private static bool ProcessFileSearch(string fileName, bool isFolder)
        {
            if (myProcessFileHashFunc != null)
            {
                if (myProcessFileHashFunc(fileName, isFolder) == false) return false;
            }
            if (isFolder == false)
            {
                myHashOptions.ResultHash += fileFuncs.MakeRelativePath(myHashOptions.Directory, fileName) + Consts.constTab +
                                            GetHashOfFile(fileName) + Consts.constTab +
                                            fileFuncs.FileSize(fileName).ToString() + Consts.constCRLF;
            }
            return true;
        }
        
        static Func<string, bool, bool> myProcessFileHashFunc = null;
        public static void MakeHash(FileHashOptions hashOptions)
        {
            MakeHash(hashOptions, null);
        }
        public static void MakeHash(FileHashOptions hashOptions, Func<string, bool, bool> func)
        {
            myHashOptions = hashOptions;
            myHashOptions.Directory = fileFuncs.NormalizeFolderName(myHashOptions.Directory);
            myHashOptions.ResultHash = "";
            myHashOptions.ResultHash += Consts.constCommentMarker + "Created on " + DateTime.Now.ToUniversalTime().ToString() + " UTC" + Consts.constCRLF;
            myHashOptions.ResultHash += Consts.constCommentMarker + "Version 1.0" + Consts.constCRLF;
            StringCollection searchPattern = StringLibs.Str2Collection(hashOptions.SearchPattern);
            myProcessFileHashFunc = func;
            fileFuncs.SearchDirectory(hashOptions.Directory, searchPattern, hashOptions.Recursive, ProcessFileSearch);
        }
        
        #endregion
    }
}